package com.yeming.tinyid.util.generator.impl;

import com.yeming.tinyid.util.bo.SegmentId;
import com.yeming.tinyid.util.enums.ResponseCodeEnum;
import com.yeming.tinyid.util.exception.TinyidException;
import com.yeming.tinyid.util.generator.ISegmentIdService;
import com.yeming.tinyid.util.generator.IdGenerator;
import com.yeming.tinyid.util.response.ResponseResult;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;

/**
 * @author yeming.gao
 * @Description: 生成id进行缓存
 * @date 2020/7/28 17:24
 */
public class CachedIdGenerator implements IdGenerator {

    /**
     * 业务类型
     */
    private String bizType;
    /**
     * 号段服务层
     */
    private ISegmentIdService iSegmentIdService;
    /**
     * 当前号段
     */
    private volatile SegmentId current;
    /**
     * 下一个号段
     */
    private volatile SegmentId next;

    private volatile boolean isLoadingNext;

    private Executor asyncTaskExecutor;

    public CachedIdGenerator(String bizType, ISegmentIdService iSegmentIdService, Executor executor) {
        this.bizType = bizType;
        this.iSegmentIdService = iSegmentIdService;
        this.asyncTaskExecutor = executor;
        loadCurrent();
    }

    private synchronized void loadCurrent() {
        if (current == null || !current.useful()) {
            //当前值为空或者超出最大值则获取新的节点
            if (next == null) {
                this.current = querySegmentId();
            } else {
                current = next;
                next = null;
            }
        }
    }

    @Override
    public Long nextId() {
        while (true) {
            if (current == null) {
                loadCurrent();
                continue;
            }
            ResponseResult<Long> result = current.nextId();
            if (result.getCode().equals(ResponseCodeEnum.OVER.getCode())) {
                //当前id值超出最大值的时候重新获取号段
                loadCurrent();
            } else {
                //当达到加载因子的时候则获取下个号段
                if (result.getCode().equals(ResponseCodeEnum.LOADING.getCode())) {
                    loadNext();
                }
                return result.getData();
            }
        }
    }

    @Override
    public List<Long> nextId(Integer batchSize) {
        List<Long> ids = new ArrayList<>();
        for (int i = 0; i < batchSize; i++) {
            Long id = nextId();
            ids.add(id);
        }
        return ids;
    }


    /**
     * 获取新的号段SegmentId
     *
     * @return SegmentId
     */
    private SegmentId querySegmentId() {
        //获取nextTinyId
        SegmentId segmentId = iSegmentIdService.getNextSegmentId(bizType);
        if (!Objects.isNull(segmentId)) {
            return segmentId;
        }
        throw new TinyidException("号段获取失败！");
    }

    /**
     * 目的是为了加对象锁，且不去影响其他
     */
    private final Object lock = new Object();

    /**
     * 加载下一个号段
     */
    private void loadNext() {
        if (next == null && !isLoadingNext) {
            synchronized (lock) {
                if (next == null && !isLoadingNext) {
                    isLoadingNext = true;
                    asyncTaskExecutor.execute(() -> {
                        try {
                            // 无论获取下个segmentId成功与否，都要将isLoadingNext赋值为false
                            next = querySegmentId();
                        } finally {
                            isLoadingNext = false;
                        }
                    });
                }
            }
        }
    }


}
